package com.asura.app.wechat.widget;

import android.animation.ValueAnimator;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.LinearInterpolator;
import android.view.animation.RotateAnimation;

import com.asura.app.wechat.R;
import com.asura.app.wechat.utils.UIUtils;

/**
 * Created by Asura on 2017/12/30.
 */

public class WechatPullToRefreshLayout extends ViewGroup {
    private static final float DRAG_RATE = .5f;//拖动速度
    private static final long REFRESH_TO_START_DURATION = 400;
    private static final long TARGET_MAX_DURATION = 400;
    private final int mTouchSlop;
    private View mRefreshView;
    private View mTarget;
    private int mTargetHiddenOffset;
    private int mLastY;
    private int mInitialMotionY;
    private boolean mIsBeingDragged;
    private int mTotalDragDistance;
    private int mMaxDragOffset;
    private boolean mRefreshing;
    private OnRefreshListener mOnRefreshListener;
    private int mRefreshViewMargin;
    private int mRefreshViewHeight;
    private ValueAnimator mRefreshToStartAnimator;
    private ValueAnimator mTargetToStartAnimator;

    public WechatPullToRefreshLayout(Context context) {
        this(context, null);
    }

    public WechatPullToRefreshLayout(Context context, AttributeSet attrs) {
        this(context, attrs, 0);
    }

    public WechatPullToRefreshLayout(Context context, AttributeSet attrs, int defStyleAttr) {
        super(context, attrs, defStyleAttr);
        ViewConfiguration configuration = ViewConfiguration.get(getContext());
        mTouchSlop = configuration.getScaledTouchSlop();
        mTargetHiddenOffset = UIUtils.dip2px(context, 100);
        mMaxDragOffset = UIUtils.dip2px(getContext(), 200);
        mTotalDragDistance = mTargetHiddenOffset + mMaxDragOffset;
        mRefreshViewMargin = UIUtils.dip2px(context, 20);
        addRefreshView();
    }

    @Override
    protected void onFinishInflate() {
        super.onFinishInflate();
        ensureTarget();
    }

    private void addRefreshView() {
        mRefreshView = LayoutInflater.from(getContext()).inflate(R.layout.view_refresh, this, false);
        addView(mRefreshView);
    }

    private void ensureTarget() {
        if (mTarget == null) {
            int childCount = getChildCount();
            if (childCount > 2) {
                throw new IllegalStateException("PullToRefreshLayout can only support one direct child");
            }
            if (childCount > 1) {
                mTarget = getChildAt(1);
                bringChildToFront(mRefreshView);
            }
        }
    }

    @Override
    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        ensureTarget();
        if (mTarget == null) return;
        measureChildren(widthMeasureSpec, heightMeasureSpec);
        int width = resolveSize(mTarget.getMeasuredWidth(), widthMeasureSpec);
        int height = resolveSize(mTarget.getMeasuredHeight(), heightMeasureSpec);
        int targetHeight = height + mTargetHiddenOffset;
        mTarget.measure(MeasureSpec.makeMeasureSpec(width, MeasureSpec.EXACTLY), MeasureSpec.makeMeasureSpec(targetHeight, MeasureSpec.EXACTLY));
        setMeasuredDimension(width, height);
    }

    @Override
    protected void onLayout(boolean changed, int l, int t, int r, int b) {
        ensureTarget();
        if (mTarget == null) return;
        mTarget.layout(0, -mTargetHiddenOffset, getMeasuredWidth(), getMeasuredHeight());
        mRefreshViewHeight = mRefreshView.getMeasuredHeight();
        mRefreshView.layout(mRefreshViewMargin, -mRefreshViewHeight, mRefreshViewMargin + mRefreshView.getMeasuredWidth(), 0);
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent event) {
        if (!isEnabled()) {
            return false;
        }
        int action = event.getAction();
        if (action == MotionEvent.ACTION_DOWN) {
            mLastY = (int) event.getY();
            mInitialMotionY = mLastY;
            mIsBeingDragged = false;
        }
        if (action == MotionEvent.ACTION_MOVE) {
            if (mIsBeingDragged) return true;
            int curY = (int) event.getY();
            int deltaY = curY - mLastY;
            if (Math.abs(deltaY) > mTouchSlop) {
                if(deltaY > 0) {
                    if(mTarget.canScrollVertically(-1)){
                        return false;
                    }
                }else {
                    //正在刷新不允许上滑target
                    if(mTarget.canScrollVertically(1) && !mRefreshing){
                        return false;
                    }
                }
                if (mTargetToStartAnimator != null) {
                    mTargetToStartAnimator.cancel();
                }
                if (mRefreshToStartAnimator != null) {
                    mRefreshToStartAnimator.cancel();
                }
                if (mOnRefreshListener != null) {
                    mOnRefreshListener.cancelRefresh();
                }
                //清掉转圈圈动画
                mRefreshView.clearAnimation();
                mRefreshing = false;
                return true;
            }
        }
        return false;
    }


    @Override
    public boolean onTouchEvent(MotionEvent event) {
        int actionMasked = event.getActionMasked();
        switch (actionMasked) {
            case MotionEvent.ACTION_MOVE:
                int curY = (int) event.getY();
                int deltaY = curY - mLastY;
                if (Math.abs(deltaY) > mTouchSlop && !mIsBeingDragged) {
                    if (deltaY > 0) {
                        deltaY -= mTouchSlop;
                    } else {
                        deltaY += mTouchSlop;
                    }
                    mIsBeingDragged = true;
                }
                if (mIsBeingDragged) {
                    moveTarget(deltaY);
                    moveSpinner(deltaY);
                    mRefreshView.setRotation(curY - mInitialMotionY);
                    mLastY = curY;
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (mIsBeingDragged) {
                    moveTargetToStart();
                    mIsBeingDragged = false;
                    int refreshViewTop = mRefreshView.getTop();
                    if (refreshViewTop == mRefreshViewMargin) {
                        startRefresh();
                    } else {
                        moveRefreshViewToStart();
                    }
                }
                break;

        }
        return true;
    }

    private void startRefresh() {
        mRefreshing = true;
        if (mOnRefreshListener != null) {
            mOnRefreshListener.onRefresh();
        }
        RotateAnimation rotateAnimation = new RotateAnimation(0, 360, RotateAnimation.RELATIVE_TO_SELF, .5f, RotateAnimation.RELATIVE_TO_SELF, .5f);
        rotateAnimation.setDuration(400);
        rotateAnimation.setRepeatCount(Animation.INFINITE);
        rotateAnimation.setFillAfter(true);
        rotateAnimation.setInterpolator(new LinearInterpolator());
        mRefreshView.startAnimation(rotateAnimation);
    }

    private void moveTargetToStart() {
        int originTop = mTarget.getTop();
        int targetDistance = Math.abs(-mTargetHiddenOffset - originTop);
        float percent = targetDistance * 1.0f / mTotalDragDistance;
        mTargetToStartAnimator = ValueAnimator.ofFloat(0, 1f);
        mTargetToStartAnimator.setDuration((long) (TARGET_MAX_DURATION * percent));
        mTargetToStartAnimator.setInterpolator(new LinearInterpolator());
        mTargetToStartAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                mTarget.offsetTopAndBottom((int) (originTop - fraction * targetDistance - mTarget.getTop()));
            }
        });
        mTargetToStartAnimator.start();
    }

    private void moveSpinner(int deltaY) {
        int refreshViewOffset = (int) (deltaY * 0.5);
        int refreshViewTop = mRefreshView.getTop();

        if (deltaY > 0) {
            //向下
            if (refreshViewTop + refreshViewOffset > mRefreshViewMargin) {
                refreshViewOffset = mRefreshViewMargin - refreshViewTop;
            }
            mRefreshView.offsetTopAndBottom(refreshViewOffset);
        } else {
            //向上
            if (refreshViewTop + refreshViewOffset < -mRefreshViewHeight) {
                refreshViewOffset = -mRefreshViewHeight - refreshViewTop;
            }

            if (mTarget.getTop() <= 0) {
                mRefreshView.offsetTopAndBottom(refreshViewOffset);
            }
        }
    }

    private void moveTarget(int deltaY) {
        int targetOffset = (int) (deltaY * DRAG_RATE);
        int targetTop = mTarget.getTop();
        if (deltaY > 0) {
            //向下
            if (targetTop + targetOffset > mMaxDragOffset) {
                targetOffset = mMaxDragOffset - targetTop;
            }
        } else {
            //向上
            if (targetTop + targetOffset < -mTargetHiddenOffset) {
                targetOffset = -mTargetHiddenOffset - targetTop;
            }
        }
        mTarget.offsetTopAndBottom(targetOffset);
    }

    public void setOnRefreshListener(OnRefreshListener listener) {
        mOnRefreshListener = listener;
    }

    public interface OnRefreshListener {
        void onRefresh();

        void cancelRefresh();
    }

    public void setRefreshing(boolean refreshing) {
        if (mRefreshing != refreshing) {
            mRefreshing = refreshing;
            if (mRefreshing) {
                // TODO: 2017/12/30  later
            } else {
                moveRefreshViewToStart();
            }
        }
    }

    private void moveRefreshViewToStart() {
        mRefreshView.clearAnimation();
        int originTop = mRefreshView.getTop();
        mRefreshToStartAnimator = ValueAnimator.ofFloat(0, 1);
        int distance = Math.abs(-mRefreshViewHeight - originTop);
        float percent = distance * 1.0f / (mRefreshViewHeight * 2 + mRefreshViewMargin);
        mRefreshToStartAnimator.setDuration((long) (REFRESH_TO_START_DURATION * percent));
        mRefreshToStartAnimator.setInterpolator(new LinearInterpolator());
        mRefreshToStartAnimator.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
            @Override
            public void onAnimationUpdate(ValueAnimator animation) {
                float fraction = animation.getAnimatedFraction();
                mRefreshView.offsetTopAndBottom((int) (originTop - fraction * (mRefreshViewMargin + mRefreshViewHeight) - mRefreshView.getTop()));
            }
        });
        mRefreshToStartAnimator.start();
    }
}
